#include "inverted_index.h"
#include <cassert>
#include <iostream>

PostingList::PostingList()
    : occur_(0)
{}

PostingList::PostingList(std::string token)
    : token_(token), occur_(0)
{}

PostingList::~PostingList()
{}

std::string PostingList::GetToken()
{
    return token_;
}

int PostingList::GetOccur()
{
    return occur_;
}

int PostingList::GetDocCount()
{
    return posting_.size();
}

int PostingList::InsertPosing(int docID, int pos)
{
    posting_[docID].push_back(pos);
    ++occur_;
    return 0;
}

int PostingList::MergePosing(PostingList &lst)
{
    if (posting_.empty()) {
        occur_ = lst.occur_;
        token_ = lst.token_;
        posting_ = lst.posting_;
        return 0;
    }

    // TODO
    assert(token_ == lst.token_);
    posting_.insert(lst.posting_.begin(), lst.posting_.end());
    occur_ += lst.occur_;

    return 0;
}

// |mem size|docID1|off_size|off1|off2|...|docID2|off_size|off1|off2...
int PostingList::Encode(unsigned char **buf, int &size)
{
    if (buf == nullptr) {
        std::cout << "encode buf is null\n";
        return -1;
    }

    int intNum = 1; // total size
    auto it = posting_.begin();
    while (it != posting_.end()) {
        // doc id
        intNum += 1;
        // offset size
        intNum += 1;
        // offset1, offset2, ...
        intNum += it->second.size();
        ++it;
    }

    int *data = new int[intNum];
    int i = 1;

    data[0] = intNum * sizeof(int);
    it = posting_.begin();
    while (it != posting_.end()) {
        // doc id
        data[i] = it->first;
        ++i;
        // offset size
        data[i] = it->second.size();
        ++i;

        // offset
        for (int j = 0; j < it->second.size(); ++j, ++i) {
            data[i] = it->second[j];
        }
        ++it;
    }

    *buf = reinterpret_cast<unsigned char *>(data);
    size = data[0];

    return 0;
}

int PostingList::Decode(unsigned char *buf, int size)
{
    if (buf == nullptr) {
        std::cout << "decode buf is null\n";
        return -1;
    }

    int *data = reinterpret_cast<int*>(buf);
    if (data[0] != size){
        std::cout << "decode buf size is error\n";
        return -1;
    }

    posting_.clear();
    occur_ = 0;

    int i = 1;
    while (i < size / sizeof(int)) {
        int docID = data[i];
        ++i;

        // offset size
        int sz = data[i];
        ++i;

        posting_.emplace(docID, std::vector<int>(sz, 0));
        for (int j = 0; j < sz; ++j) {
            posting_[docID][j] = data[i];
            ++occur_;
            ++i;
        }
    }

    return 0;
}

InvertedIndex::InvertedIndex()
    : doc_count_(0), threshold_(0)
{}

InvertedIndex::InvertedIndex(int threshold)
    : doc_count_(0), threshold_(threshold)
{}

InvertedIndex::~InvertedIndex()
{}

int InvertedIndex::AddDoc(int count)
{
    doc_count_ += count;
    return 0;
}

int InvertedIndex::AddPosting(std::string &token, int docID, int pos)
{
    if (index_.count(token) == 0) {
        index_.emplace(token, token);
    }
    index_[token].InsertPosing(docID, pos);

    return 0;
}

int InvertedIndex::MergeIndex(InvertedIndex &index)
{
    if (index_.empty()) {
        index_ = index.index_;
        doc_count_ = index.doc_count_;
        return 0;
    }

    auto it = index.index_.begin();
    while (it != index.index_.end()) {
        std::string token = it->second.GetToken();
        index_[token].MergePosing(it->second);
        ++it;
    }
    doc_count_ += index.doc_count_;

    return 0;
}